package com.lin.fengxiu.service;

import com.github.wxpay.sdk.LinWxPayConfig;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.lin.fengxiu.core.LocalUser;
import com.lin.fengxiu.exception.http.ForbiddenException;
import com.lin.fengxiu.exception.http.NotFoundException;
import com.lin.fengxiu.exception.http.ParameterException;
import com.lin.fengxiu.exception.http.ServerErrorException;
import com.lin.fengxiu.model.Order;
import com.lin.fengxiu.repository.OrderRepository;
import com.lin.fengxiu.util.CommonUtil;
import com.lin.fengxiu.util.HttpRequestProxy;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.tags.Param;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @ClassName WxPaymentService
 * @Description TODO
 * @Author wushaopei
 * @Date 2021/4/10 18:09
 * @Version 1.0
 */
@Service
public class WxPaymentService {

    @Autowired
    private OrderRepository orderRepository;

    @Value("${fengxiu.order.pay-callback-host}")
    private String payCallbackHost;

    @Value("${fengxiu.order.pay-callback-path}")
    private String payCallbackPath;

    @Autowired
    private OrderService orderService;

    private static LinWxPayConfig linWxPayConfig = new LinWxPayConfig();

    /**
     * @Description TODO 预订单业务，在微信服务器生成预订单
     * @param oid 小程序待支付订单
     * @return
     */
    public Map<String,String> preOrder(Long oid){

        Long uid = LocalUser.getUser().getId();
        // 查询订单并判断订单是否存在
        Optional<Order> optionalOrder = this.orderRepository.findFirstByUserIdAndId(uid,oid);
        Order order = optionalOrder.orElseThrow(
                ()-> new NotFoundException(50009));
        if(order.needCancel()){
            throw new ForbiddenException(50010);
        }
        // 订单有效，这里发起下单请求（预订单）
        WXPay wxPay = this.assmbleWxPayConfig();
        // 统一下单， unifiedOrder 中需要传入订单信息，参数类型为Map<String,String>
        Map<String, String> params = this.makePreOrderParams(order.getFinalTotalPrice(),order.getOrderNo());
        Map<String, String> wxOrder ;
        try {
            // 统一下单并返回结果
            wxOrder = wxPay.unifiedOrder(params);
        }catch (Exception e){
            throw new ServerErrorException(9999);
        }

        // 判断统一下单是否成功，下单成功则获取 prepay_id
        if(this.unifiedOrderSuccess(wxOrder)){
            // 插入prepay_id到模型
            this.orderService.updateOrderPrepayId(order.getId(),wxOrder.get("prepay_id"));
        }

        // prepay_id
        // wx.requestPayment
        return makePaySignature(wxOrder);
    }

    /**
     * @Description TODO 组装支付参数与签名 —— 用于拉取支付
     * @param wxOrder
     * @return
     */
    private Map<String, String> makePaySignature(Map<String, String> wxOrder){
        Map<String,String> wxPayMap = new HashMap<>();
        // prepay_id 必须以 key=value 的方式进行组装
        String packages = "prepay_id=" + wxOrder.get("prepay_id");
        // appId只用于生成签名，返回给前端时要删除掉
        wxPayMap.put("appId", WxPaymentService.linWxPayConfig.getAppID());
        // 时间戳 ，这里必须为10位的，需要转换
        wxPayMap.put("timeStamp", CommonUtil.timestamp10());
        wxPayMap.put("nonceStr", RandomStringUtils.randomAlphabetic(32));
        wxPayMap.put("package",packages);     //  prepay_id
        wxPayMap.put("signType", "HMAC-SHA256"); // 签名算法

        String sign;
        try {
            // 调用 WXPayUtil根据支付参数生成签名
            sign = WXPayUtil.generateSignature(wxPayMap, WxPaymentService.linWxPayConfig.getKey(), WXPayConstants.SignType.HMACSHA256);
        }catch (Exception e){
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }

        Map<String,String> miniPayParams = new HashMap<>();

        // 组装签名及支付参数
        miniPayParams.put("paysign", sign);
        miniPayParams.putAll(wxPayMap);
        miniPayParams.remove("appId");
        return miniPayParams;
    }

    /**
     * @Description TODO 统一下单回调结果校验
     * @param wxOrder
     * @return
     */
    private boolean unifiedOrderSuccess(Map<String, String> wxOrder){
        if(!wxOrder.get("return_code").equals("SUCCESS")
            || !wxOrder.get("result_code").equals("SUCCESS")){
            throw new ParameterException(10007);
        }
        return true;
    }


    /**
     * @Description TODO 组装订单数据
     * @param serverFinalPrice
     * @param orderNo
     * @return
     */
    private Map<String, String> makePreOrderParams(BigDecimal serverFinalPrice, String orderNo){

        Map<String,String> data = new HashMap<>();
        data.put("body","Sleeve");  // 商品描述
        data.put("out_trade_no",orderNo);  // 订单号
        data.put("device_info","Sleeve");  //设备号
        data.put("fee_type","CNY");   // 币种
        data.put("trade_type","JSAPI");  // 交易类型

        // 标价金额，单位为分，需要作转换
        data.put("total_fee", CommonUtil.yuanToFenPlainString(serverFinalPrice));
        data.put("open_id", LocalUser.getUser().getOpenid()); // 用户标识
        // 终端IP，通过HttpRequestProxy获取用户远程终端地址
        data.put("spbill_create_ip", HttpRequestProxy.getRemoteRealIp());
        // 通知地址
        String payCallbackUrl = this.payCallbackHost + this.payCallbackPath;
        data.put("notify_url",payCallbackUrl);
        return data;
    }


    /**
     * @Description 填充配置参数到 WxPay 类
     * @return
     */
    public WXPay assmbleWxPayConfig(){
        WXPay wxPay = null;
        try {
            wxPay = new WXPay(WxPaymentService.linWxPayConfig);
        }catch (Exception e){
            throw new ServerErrorException(9999);
        }
        return wxPay;
    }


}
